'''
遗传算法拟合数字图像
'''

from PIL import Image
import numpy as np


##计算适应度=-方差
def CalcFitness(genes, target):
    s = np.square(genes.astype(np.int32) - target.astype(np.int32)).sum(axis=-1).sum(axis=-1)
    #print('适应度', -s)
    index = np.argsort(s)  # 适应度排序的序号数组
    #print(index)
    return -s, index


# 选择


# 保留适应度最高的fiti[0]
def selection(fiti):
    '''
    联赛选择
    :param fiti: 适应度排序的序号数组
    :return: 选中的序号
    '''

    def s1(n, m=2):
        '''
        从n个选择一个，m选择压，返回选择的序号
        '''
        return np.random.randint(0, n, m).min()

    s = np.size(fiti)
    r = np.empty_like(fiti)
    for i in range(s):
        ri = fiti[s1(s)]
        r[i] = ri
    #print('选择', r)
    return r


def AcrChrom(PG, sel):
    '''
    交叉
    :param PG:
    :param sel:
    :return:
    '''

    def acr(c1, c2, v1, v2):
        # 交叉
        # 0.9概率
        # 返回深复制
        (x, y) = c1.shape
        ra=np.random.rand()
        if ra < 0.3:
            x1 = np.random.randint(0, x)
            x2 = np.random.randint(0, x)
            if x1 > x2:
                x1, x2 = x2, x1
            v1[:x1, :] = c2[:x1, :]
            v1[x1:x2, :] = c1[x1:x2, :]
            v1[x2:, :] = c2[x2:, :]
            v2[:x1, :] = c1[:x1, :]
            v2[x1:x2, :] = c2[x1:x2, :]
            v2[x2:, :] = c1[x2:, :]
        elif 0.3<ra<0.6:
            x1 = np.random.randint(0, y)
            x2 = np.random.randint(0, y)
            if x1 > x2:
                x1, x2 = x2, x1
            v1[:, :x1] = c2[:, :x1]
            v1[:, x1:x2] = c1[:, x1:x2]
            v1[:, x2:] = c2[:, x2:]
            v2[:, :x1] = c1[:, :x1]
            v2[:, x1:x2] = c2[:, x1:x2]
            v2[:, x2:] = c1[:, x2:]
        elif 0.6<ra<0.9:
            x1 = np.random.randint(0, x)
            x2 = np.random.randint(0, x)
            if x1 > x2:
                x1, x2 = x2, x1

            y1 = np.random.randint(0, y)
            y2 = np.random.randint(0, y)

            v1 = c1
            v2 = c2

            v1[x1:x2,y1:y2]=c2[x1:x2,y1:y2]
            v2[x1:x2, y1:y2] = c1[x1:x2, y1:y2]

        else:
            v1 = c1
            v2 = c2

    s = np.size(sel)
    PG2 = np.empty_like(PG)
    i = 2
    while i < s:
        acr(PG[sel[i]], PG[sel[i+1]], PG2[i], PG2[i + 1])
        i += 2
    return PG2


def mutation(PG,p):#p为变异率
    n, a, b = PG.shape
    n=n-2
    m=n*a*b
    mu = m*p
    sigma = mu*(1-p)
    ra= sigma * np.random.randn() + mu
    ra=int(ra)
    # print(ra)
    if ra<=0 :
        ra=1
    elif ra>=m:
        ra=m

    for i in range(ra):
        PG[np.random.randint(2, n+2)][np.random.randint(0, a)][np.random.randint(0, b)] = np.random.randint(0, 256)

    return ra

####

G = 0
FES = 0
N = 100  # 每代个体数 偶数
Gmax = 10000 # 最大迭代次数
rate=0.05 #初始变异率

##读取图像
im = np.array(Image.open('0_0.bmp'))

# 随机初始化
(a, b) = im.shape
shape3d = (N, a, b)
PG = np.random.randint(0, 256, shape3d, dtype=im.dtype)
rate0=rate
fitn = []
for i in range(Gmax+1):
    fitG, fiti = CalcFitness(PG, im)
    bestfit = PG[fiti[0]].copy()
    u = (np.mean(fitG), fitG[fiti[0]])
 #   fitn.append(u)
    sel = selection(fiti)
    PG = AcrChrom(PG, sel)
    ra= mutation(PG,rate)
   # rate= (1 - (1.0 * i / Gmax)) * rate0
   # rate = (1 - (1.0 * i / Gmax))**2 * rate0
    rate = (1 - (10.0  / Gmax)) * rate
    PG[0]=bestfit
    PG[1] = bestfit

    if i % 50 == 0 :
        print('代数 ', i, '变异数', ra, '适应度(平均，最高)', u)
        img=Image.fromarray(bestfit)
        img.save('./png/0_{}.png'.format(i))

print('结束')
#print('适应度变化', fitn)
